<template>
  <div class="app-container">
    <div class="search-container">
      <el-form ref="queryFormRef" :model="queryParams" :inline="true">
        <el-form-item label="关键字" prop="keywords">
          <el-input
              v-model="queryParams.keywords"
              placeholder="菜单名称"
              clearable
              @keyup.enter="handleQuery"
          />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery"
          >
            <template #icon>
              <i-ep-search/>
            </template>
            搜索
          </el-button
          >
          <el-button @click="handleResetQuery">
            <template #icon>
              <i-ep-refresh/>
            </template>
            重置
          </el-button
          >
        </el-form-item>
      </el-form>
    </div>

    <el-card shadow="never" class="table-container">
      <template #header>
        <el-button
            v-hasPerm="['sys:menu:add']"
            type="success"
            @click="handleDialogOpen(undefined,0)"
        >
          <template #icon>
            <i-ep-plus/>
          </template>
          新增
        </el-button>

        <el-button
            type="success"
            @click="refreshMenuCache">
          <template #icon>
            <i-ep-refresh/>
          </template>
          刷新菜单缓存
        </el-button>
      </template>

      <el-table-v2
          v-model:expanded-row-keys="expandedRowKeys"
          :columns="columns"
          :data="menuTableData"
          :h-scrollbar-size="10"
          :width="screenWidth-230"
          expand-column-key="name"
          :height="500"
          :fixed="true"
      />

    </el-card>

    <el-dialog
        v-model="dialog.visible"
        :title="dialog.title"
        destroy-on-close
        append-to-body
        width="1000px"
        @close="closeDialog"
    >
      <el-form
          ref="menuFormRef"
          :model="formData"
          :rules="rules"
          label-width="160px"
      >
        <el-form-item label="父级菜单" prop="parentId">
          <el-tree-select
              v-model="formData.parentId"
              placeholder="选择上级菜单"
              :data="menuOptions"
              filterable
              check-strictly
              :render-after-expand="false"
          />
        </el-form-item>

        <el-form-item label="菜单名称" prop="name">
          <el-input v-model="formData.name" placeholder="请输入菜单名称"/>
        </el-form-item>

        <el-form-item label="菜单类型" prop="type">
          <el-radio-group
              v-model="formData.type"
              @change="handleMenuTypeChange"
          >
            <el-radio value="CATALOG">目录</el-radio>
            <el-radio value="MENU">菜单</el-radio>
            <el-radio value="BUTTON">按钮</el-radio>
            <el-radio value="EXTLINK">外链</el-radio>
          </el-radio-group>
        </el-form-item>

        <el-form-item
            v-if="formData.type == 'EXTLINK'"
            label="外链地址"
            prop="path"
        >
          <el-input v-model="formData.path" placeholder="请输入外链完整路径"/>
        </el-form-item>

        <el-form-item
            v-if="
            formData.type == MenuTypeEnum.CATALOG ||
            formData.type == MenuTypeEnum.MENU ||
            formData.type == MenuTypeEnum.BUTTON
          "
            label=""
            prop="path"
        >
          <template #label>
            <div>
              路由路径
              <el-tooltip placement="bottom" effect="light">
                <template #content>
                  定义应用中不同页面对应的 URL 路径，目录需以 /
                  开头，菜单项不用。例如：系统管理目录
                  /system，系统管理下的用户管理菜单 user。
                </template>
                <i-ep-QuestionFilled class="inline-block"/>
              </el-tooltip>
            </div>
          </template>
          <el-input
              v-if="formData.type == MenuTypeEnum.CATALOG"
              v-model="formData.path"
              placeholder="system"
          />
          <el-input v-else v-model="formData.path" placeholder="user"/>
        </el-form-item>

        <el-form-item
            v-if="formData.type == MenuTypeEnum.MENU"
            prop="component"
        >
          <template #label>
            <div>
              组件路径
              <el-tooltip placement="bottom" effect="light">
                <template #content>
                  组件页面完整路径，相对于 src/views/，如
                  system/user/index，缺省后缀 .vue
                </template>
                <i-ep-QuestionFilled class="inline-block"/>
              </el-tooltip>
            </div>
          </template>

          <el-input
              v-model="formData.component"
              placeholder="system/user/index"
              style="width: 95%"
          >
            <template v-if="formData.type == MenuTypeEnum.MENU" #prepend
            >src/views/
            </template
            >
            <template v-if="formData.type == MenuTypeEnum.MENU" #append
            >.vue
            </template
            >
          </el-input>
        </el-form-item>

        <el-form-item v-if="formData.type == MenuTypeEnum.MENU">
          <template #label>
            <div>
              路由参数
              <el-tooltip placement="bottom" effect="light">
                <template #content>
                  不需要以?开头，如：key=value&key2=value2
                </template>
                <i-ep-QuestionFilled class="inline-block"/>
              </el-tooltip>
            </div>
          </template>
          <el-input
              v-model="formData.params"
              placeholder=""
              style="width: 95%"
          ></el-input>
        </el-form-item>

        <el-form-item
            v-if="formData.type !== MenuTypeEnum.BUTTON"
            prop="visible"
            label="显示状态"
        >
          <el-radio-group v-model="formData.visible">
            <el-radio :value="1">显示</el-radio>
            <el-radio :value="0">隐藏</el-radio>
          </el-radio-group>
        </el-form-item>

        <el-form-item
            v-if="
            formData.type === MenuTypeEnum.CATALOG ||
            formData.type === MenuTypeEnum.MENU
          "
        >
          <template #label>
            <div>
              始终显示
              <el-tooltip placement="bottom" effect="light">
                <template #content>
                  当设置为始终显示时，即使只有一个子菜单也会显示<br/>
                  叶子节点的菜单，请选择“否”。
                </template>
                <i-ep-QuestionFilled class="inline-block"/>
              </el-tooltip>
            </div>
          </template>

          <el-radio-group v-model="formData.alwaysShow">
            <el-radio :value="1">是</el-radio>
            <el-radio :value="0">否</el-radio>
          </el-radio-group>
        </el-form-item>

        <el-form-item
            v-if="formData.type === MenuTypeEnum.MENU"
            label="页面缓存"
        >
          <el-radio-group v-model="formData.keepAlive">
            <el-radio :value="1">开启</el-radio>
            <el-radio :value="0">关闭</el-radio>
          </el-radio-group>
        </el-form-item>

        <el-form-item label="排序" prop="sort">
          <el-input-number
              v-model="formData.sort"
              style="width: 100px"
              controls-position="right"
              :min="0"
          />
        </el-form-item>

        <!-- 权限标识 -->
        <el-form-item
            label="权限标识"
            prop="perm"
        >
          <el-input v-model="formData.perm" placeholder="sys:user:add"/>
        </el-form-item>

        <el-form-item
            v-if="formData.type !== MenuTypeEnum.BUTTON"
            label="图标"
            prop="icon"
        >
          <!-- 图标选择器 -->
          <icon-select v-model="formData.icon"/>
        </el-form-item>

        <el-form-item
            v-if="formData.type == MenuTypeEnum.CATALOG"
            label="跳转路由"
        >
          <el-input v-model="formData.redirect" placeholder="跳转路由"/>
        </el-form-item>
      </el-form>

      <template #footer>
        <div class="dialog-footer">
          <el-button :loading="requestStore.isRequestPending" type="primary" @click="submitForm">确 定</el-button>
          <el-button @click="closeDialog">取 消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="tsx">
import {useRequestStore} from "@/store";

defineOptions({
  name: "Menu",
  inheritAttrs: false,
});
import MenuAPI from "@/api/menu";
import {MenuQuery, MenuForm, MenuVO} from "@/api/menu/model";
import {MenuTypeEnum} from "@/enums/MenuTypeEnum";

import {ref, resolveComponent} from 'vue'
import {
  TableV2FixedDir,
  ElButton,
  ElIcon,
  ElTag,
  ElTooltip, ExpandedRowsChangeHandler, RowExpandHandler
} from 'element-plus'
const requestStore = useRequestStore();
const screenWidth = ref(window.innerWidth);
const onResize = () => {
  screenWidth.value = window.innerWidth;
};

onMounted(() => {
  window.addEventListener('resize', onResize);
});

const expandedRowKeys = ref<string[]>([])
// 计算 icon 组件
const iconComponent = function (icon: any) {
  if (icon && icon.startsWith('el-icon')) {
    const iconName = icon.replace('el-icon-', '');
    const IconComponent = resolveComponent(iconName);
    return <ElIcon class="sub-el-icon">
      <IconComponent/>
      <component is={iconName}/>
    </ElIcon>;
  } else if (icon) {
    return <svgIcon icon-class={icon}/>;
  } else {
    return '';
  }
}

const columns = [
  {
    dataKey: "name",
    key: "name",
    title: "菜单名称",
    width: 200,
    cellRenderer: (props: CellRenderProps) => (
        <>
          {iconComponent(props.rowData.icon)}
          {props.rowData.name}
        </>
    )
  }, {
    dataKey: "type",
    key: "type",
    title: "类型",
    width: 80,
    cellRenderer: (props: CellRenderProps) => (
        <>
          {props.rowData.type === MenuTypeEnum.CATALOG ? <ElTag type="warning">目录</ElTag> : ""}
          {props.rowData.type === MenuTypeEnum.MENU ? <ElTag type="success">菜单</ElTag> : ""}
          {props.rowData.type === MenuTypeEnum.BUTTON ? <ElTag type="danger">按钮</ElTag> : ""}
          {props.rowData.type === MenuTypeEnum.EXTLINK ? <ElTag type="info">外链</ElTag> : ""}
        </>
    )
  }, {
    dataKey: "path",
    key: "path",
    title: "路由路径",
    width: 250
  }, {
    dataKey: "component",
    key: "component",
    title: "组件路径",
    width: 250
  }, {
    dataKey: "perm",
    key: "perm",
    title: "权限标识",
    width: 200
  }, {
    dataKey: "visible",
    key: "visible",
    title: "状态",
    width: 50,
    cellRenderer: (props: CellRenderProps) => (
        <>
          {props.rowData.visible === 1 ? <ElTag type="success">显示</ElTag> : <ElTag type="info">隐藏</ElTag>}
        </>
    )
  }, {
    dataKey: "sort",
    key: "sort",
    title: "排序",
    width: 50
  }, {
    fixed: TableV2FixedDir.RIGHT,
    dataKey: "visible",
    key: "visible",
    title: "操作",
    width: 200,
    cellRenderer: (props: CellRenderProps) => (
        <>
          {props.rowData.type === MenuTypeEnum.BUTTON ? "" :
              <ElButton
                  v-hasPerm="sys:menu:add"
                  type="primary" link size="small"
                  onClick={() => {
                    handleDialogOpen(undefined, props.rowData.id)
                  }}
              >
                <i-ep-plus/>
                新增</ElButton>}

          <ElButton
              v-hasPerm="sys:menu:edit"
              type="primary" link size="small"
              onClick={() => {
                handleDialogOpen(props.rowData)
              }}
          >
            <i-ep-edit/>
            编辑</ElButton>
          <ElButton v-hasPerm="sys:menu:delete"
                    type="danger" link size="small"
                    onClick={() => {
                      handleDelete(props.rowData.id)
                    }}
          >
            <i-ep-delete/>
            删除
          </ElButton>
        </>
    ),
  },
]

const queryFormRef = ref(ElForm);
const menuFormRef = ref(ElForm);

const loading = ref(false);
const dialog = reactive({
  title: "新增菜单",
  visible: false,
});

// 查询参数
const queryParams = reactive<MenuQuery>({});
// 菜单表格数据
const menuTableData = ref<MenuVO[]>([]);
// 顶级菜单下拉选项
const menuOptions = ref<OptionType[]>([]);

// 初始菜单表单数据
const initialMenuFormData = ref<MenuForm>({
  id: undefined,
  parentId: 0,
  visible: 1,
  sort: 1,
  type: MenuTypeEnum.MENU, // 默认“菜单”
  alwaysShow: 0,
  keepAlive: 1,
  params: undefined,
});

function refreshMenuCache() {
  MenuAPI.refreshCache()
      .then((response) => {
        ElMessage.success("刷新成功");
        handleQuery();
      })
      .catch((error) => {
        ElMessage.error("刷新失败");
      });
}

function resetMenuFormData(): MenuForm {
  const resetMenu = ref<MenuForm>({
    id: undefined,
    parentId: 0,
    visible: 1,
    sort: 1,
    type: MenuTypeEnum.MENU, // 默认“菜单”
    alwaysShow: 0,
    keepAlive: 1,
    params: undefined,
  });
  return resetMenu.value;
}

// 菜单表单数据
const formData = ref({...initialMenuFormData.value});

// 表单验证规则
const rules = reactive({
  parentId: [{required: true, message: "请选择顶级菜单", trigger: "blur"}],
  name: [{required: true, message: "请输入菜单名称", trigger: "blur"}],
  type: [{required: true, message: "请选择菜单类型", trigger: "blur"}],
  path: [{required: true, message: "请输入路由路径", trigger: "blur"}],
  component: [{required: true, message: "请输入组件路径", trigger: "blur"}],
  visible: [{required: true, message: "请输入路由路径", trigger: "blur"}],
});

// 选择表格的行菜单ID
const selectedMenuId = ref<number | undefined>();

// 查询
function handleQuery() {
  loading.value = true;
  MenuAPI.getList(queryParams)
      .then((response) => {
        menuTableData.value = response.data;
      })
      .finally(() => {
        loading.value = false;
      });
}

// 重置查询
function handleResetQuery() {
  queryFormRef.value.resetFields();
  handleQuery();
}

// 行点击事件
function handleRowClick(row: MenuVO) {
  // 记录表格选择的菜单ID，新增子菜单作为父菜单ID
  selectedMenuId.value = row.id;
}

/**
 * 打开表单弹窗
 *
 * @param parentId 父菜单ID
 * @param menuId 菜单ID
 */
function handleDialogOpen(row?: any, parentId?: number) {
  MenuAPI.getOptions()
      .then((response) => {
        let data: any = response?.data
        menuOptions.value = [{value: 0, label: "顶级菜单", children: data}];
      })
      .then(() => {
        dialog.visible = true;
        if (row && row.id) {
          dialog.title = "编辑菜单";
          setTimeout(() => {
            let data = Object.assign({}, row)
            if (data.params) {
              data.params = decodeURIComponent(decodeURIComponent(data.params))
            }
            initialMenuFormData.value = {...data};
            formData.value = data;
          });
        } else {
          let resetMenu: MenuForm = resetMenuFormData()
          initialMenuFormData.value = resetMenu;
          formData.value = resetMenu;
          dialog.title = "新增菜单";
          formData.value.parentId = parentId;
        }
      });
}

// 菜单类型切换
function handleMenuTypeChange() {
  // 如果菜单类型改变
  if (formData.value.type !== initialMenuFormData.value.type) {
    if (formData.value.type === MenuTypeEnum.MENU) {
      // 目录切换到菜单时，清空组件路径
      if (initialMenuFormData.value.type === MenuTypeEnum.CATALOG) {
        formData.value.component = "";
      } else {
        // 其他情况，保留原有的组件路径
        formData.value.path = initialMenuFormData.value.path;
        formData.value.component = initialMenuFormData.value.component;
      }
    }
  }
}

/** 菜单保存提交 */
function submitForm() {
  menuFormRef.value.validate((isValid: boolean) => {
    if (isValid) {
      const menuId = formData.value.id;
      let params = formData.value.params;
      if (params) {
        params = params.replace(/\s+/g, '');
        if (params.startsWith("?")) {
          params = params.substring(1, params.length - 1)
        }
        params = encodeURIComponent(encodeURIComponent(params))
        formData.value.params = params;
      }
      if (menuId) {
        MenuAPI.update(formData.value).then((response) => {
          if (response.status) {
            ElMessage.success("修改成功");
            closeDialog();
            handleQuery();
          }
        });
      } else {
        MenuAPI.add(formData.value).then((response) => {
          if (response.status) {
            ElMessage.success("新增成功");
            closeDialog();
            handleQuery();
          }
        });
      }
    }
  });
}

// 删除菜单
function handleDelete(menuId: number) {
  if (!menuId) {
    ElMessage.warning("请勾选删除项");
    return false;
  }

  ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        MenuAPI.deleteById(menuId).then((response) => {
          if (response.status) {
            ElMessage.success("删除成功");
            handleQuery();
          }
        });
      })
      .catch(() => ElMessage.info("已取消删除"));
}

// 关闭弹窗
function closeDialog() {
  dialog.visible = false;
  menuFormRef.value.resetFields();
  menuFormRef.value.clearValidate();
}

onMounted(() => {
  handleQuery();
});
</script>

<style lang="scss" scoped>
.sub-el-icon {
  width: 14px !important;
  margin-right: 0 !important;
  font-size: 14px !important;
  color: currentcolor;
}

.hideSidebar {
  .el-sub-menu,
  .el-menu-item {
    .svg-icon,
    .sub-el-icon {
      margin-left: 20px;
    }
  }
}
</style>
